package com.jinyu.gateway.config;

import com.jinyu.core.codec.Decoder;
import com.jinyu.core.codec.Encoder;
import com.jinyu.core.codec.MyLengthFieldBasedFrameDecoder;
import com.jinyu.core.codec.MyLengthFieldPrepender;
import com.jinyu.gateway.server.NettyServer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.EventExecutorGroup;
import lombok.Data;
import org.springframework.beans.BeansException;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.ThreadFactory;

@Configuration
@Data
@ConfigurationProperties("netty.server")
public class NettyConfig implements ApplicationContextAware {
    private int port;
    private int backLog;

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        NettyConfig.applicationContext = applicationContext;
    }

    @Bean
    public NettyServer nettyServer() throws InterruptedException {
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        int cpuCoreNum = Runtime.getRuntime().availableProcessors();
        EventExecutorGroup executors = new DefaultEventLoopGroup(cpuCoreNum + 1);
        NioEventLoopGroup bossGroup = new NioEventLoopGroup(1, r -> {
            return new Thread(r, "boss-" + System.currentTimeMillis());
        });

        NioEventLoopGroup workGroup = new NioEventLoopGroup(cpuCoreNum + 1, r -> {
            return new Thread(r, "work-" + System.currentTimeMillis());
        });
        serverBootstrap.group(bossGroup, workGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 1024)
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                .childOption(ChannelOption.TCP_NODELAY, true)
                .childOption(ChannelOption.SO_RCVBUF, 1024 * 1024)
                .childOption(ChannelOption.SO_SNDBUF, 1024 * 1024)
//                .handler(new LoggingHandler(LogLevel.DEBUG))
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        socketChannel.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
                        //字符串编码器，一定要加在SimpleClientHandler 的上面
                        socketChannel.pipeline().addLast(new IdleStateHandler(30, 0, 0));
                        socketChannel.pipeline().addLast(new MyLengthFieldBasedFrameDecoder());
                        socketChannel.pipeline().addLast(new Decoder());
                        socketChannel.pipeline().addLast(new MyLengthFieldPrepender());
                        socketChannel.pipeline().addLast(new Encoder());
                        socketChannel.pipeline().addLast(executors, (ChannelHandler) applicationContext.getBean("protocolDispatchHandler"));
                    }
                });
        ChannelFuture future = serverBootstrap.bind(port).sync();
        future.channel().closeFuture().addListener((ChannelFutureListener) channelFuture -> {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        });
        return new NettyServer(future.channel());
    }
}
